package com.ns002.yigou.quote.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.ns002.yigou.quote.common.app.QuoteDate;
import com.ns002.yigou.quote.dao.KLineDao;
import com.ns002.yigou.quote.dao.impl.SqlExecuteDao;
import com.ns002.yigou.quote.domain.OpenCloseDate;
import com.ns002.yigou.quote.domain.Product;
import com.ns002.yigou.quote.entity.KLine;
import com.ns002.yigou.quote.exception.LogicException;
import com.ns002.yigou.quote.globals.RedisKey;
import com.opslab.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author xu
 * @Date 2017/7/28 0028 上午 11:19
 * K线数据逻辑
 */
@Service
public class KLineService {

    @Autowired
    SqlExecuteDao sqlExecuteDao;

    @Autowired
    KLineDao kLineDao;

    @Autowired
    ProductService productService;

    @Autowired
    QuoteDate quoteDate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static Logger log = LoggerFactory.getLogger("kline-service");

    /**
     * 获取K线节点
     *
     * @param contract  标识
     * @param quoteDate 行情时间点
     * @param type      K线类型
     * @return
     */
    public KLine findKLineNode(String contract, Date quoteDate, String type) {
        switch (type) {
            case "M1":
                return sqlExecuteDao.findKLineNodeBy1M(contract, quoteDate);
            case "M5":
                return sqlExecuteDao.findKLineNodeBy5M(contract, quoteDate);
            case "M15":
                return sqlExecuteDao.findKLineNodeBy15M(contract, quoteDate);
            case "M30":
                return sqlExecuteDao.findKLineNodeBy30M(contract, quoteDate);
            case "H1":
                return sqlExecuteDao.findKLineNodeBy1H(contract, quoteDate);
            case "H2":
                return sqlExecuteDao.findKLineNodeBy2H(contract, quoteDate);
            case "H4":
                return sqlExecuteDao.findKLineNodeBy4H(contract, quoteDate);
            case "D1":
                return sqlExecuteDao.findKLineNodeBy1D(contract, quoteDate);
        }

        throw new LogicException("K线类型错误");
    }

    //{"AUD":1502170186460,"JPY":1502170208961,"EUR":1502170166957,"GBP":1502170130458,"DINIW":1502170210058,"NZD":1502170204961}

    /**
     * 保存
     *
     * @param kLine
     */
    public void save(KLine kLine) {
        kLineDao.save(kLine);
    }

    private KLine emptyKLineNode(String contract, Date startDate) {
        KLine kLine = new KLine();
        kLine.setContract(contract);
        kLine.setStartDate(startDate);
        kLine.setClose(-1D);

        return kLine;
    }

    /**
     * 更新分时图
     */
    public void updateM1Cache() {
        List<Product> productList = productService.findProductList();
        for (Product product : productList) {
            try {
                OpenCloseDate openCloseDate = QuoteDate.getPreviousOpenCloseDateByAlias(product.getContract());
                List<KLine> lines = kLineDao.findKlineM1(product.getContract(), openCloseDate.getOpenDate(), openCloseDate.getCloseDate());

                //处理断点和跳点
                List<KLine> newLines = handleBreak(lines, product.getContract());
                List<KLine> openJumeList = handleJume(lines, openCloseDate.getOpenDate(), product.getContract());
                openJumeList.addAll(newLines);
                lines = openJumeList;

                Map<String, Object> klineData = new HashMap<>();

                List<String> dateList = new ArrayList<>();
                List<String> dataList = new ArrayList<>();

                for (KLine line : lines) {
                    dateList.add(dateConvHHmm(line.getStartDate()));
                    dataList.add(String.valueOf(line.getClose()));
                }

                klineData.put("dateList", dateList);
                klineData.put("dataList", dataList);

                //加载到缓存
                stringRedisTemplate.opsForValue().set(RedisKey.KLINE_M1 + "-" + product.getContract(), JSON.toJSONString(klineData));

            } catch (Exception e) {
                log.error("分时图查询失败" + product.getContract(), e);
            }

        }
    }

    /**
     * 更新K线行情
     *
     * @param type
     */
    public void updateKLineCache(String type) {
        List<Product> productList = productService.findProductList();

        for (Product product : productList) {
            try {

                List<KLine> lines;
                switch (type) {
                    case "M5":
                        lines = kLineDao.findkline(product.getContract(), 2);
                        break;
                    case "M15":
                        lines = kLineDao.findkline(product.getContract(), 3);
                        break;
                    case "M30":
                        lines = kLineDao.findkline(product.getContract(), 4);
                        break;
                    case "H1":
                        lines = kLineDao.findkline(product.getContract(), 5);
                        break;
                    case "H2":
                        lines = kLineDao.findkline(product.getContract(), 6);
                        break;
                    case "H4":
                        lines = kLineDao.findkline(product.getContract(), 7);
                        break;
                    case "D1":
                        lines = kLineDao.findkline(product.getContract(), 8);
                        break;
                    default:
                        throw new Exception("无法识别类型" + product.getContract());
                }

                Map<String, Object> klineData = new HashMap<>();
                List<String> dateList = new ArrayList<>();
                List<List> dataList = new ArrayList<>();

                for (int i = (lines.size() - 1); i >= 0; i--) {
                    KLine line = lines.get(i);
                    dateList.add(DateUtil.dateTime(line.getStartDate()));

                    List<String> knode = new ArrayList<>();
                    knode.add(String.valueOf(line.getOpen()));
                    knode.add(String.valueOf(line.getClose()));
                    knode.add(String.valueOf(line.getLow()));
                    knode.add(String.valueOf(line.getHigh()));
                    dataList.add(knode);
                }

                klineData.put("dateList", dateList);
                klineData.put("dataList", dataList);

                String key;
                switch (type) {
                    case "M5":
                        key = RedisKey.KLINE_M5;
                        break;
                    case "M15":
                        key = RedisKey.KLINE_M15;
                        break;
                    case "M30":
                        key = RedisKey.KLINE_M30;
                        break;
                    case "H1":
                        key = RedisKey.KLINE_H1;
                        break;
                    case "H2":
                        key = RedisKey.KLINE_H2;
                        break;
                    case "H4":
                        key = RedisKey.KLINE_H4;
                        break;
                    case "D1":
                        key = RedisKey.KLINE_D1;
                        break;
                    default:
                        throw new Exception("无法识别类型" + product.getContract());
                }

                key = key + "-" + product.getContract();

                //更新缓存
                stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(klineData));

            } catch (Exception e) {
                log.error("K线图查询失败-" + type + "-" + product.getContract(), e);
            }
        }
    }

    SimpleDateFormat dateFormatHHmm = new SimpleDateFormat("HH:mm");

    private synchronized String dateConvHHmm(Date date) {
        return dateFormatHHmm.format(date);
    }

    /**
     * 获取最后的K线点位
     * @param type      K线类型
     * @param contract  标识
     * @return
     */
    public KLine findKLineLastNode(String type, String contract){
        long space = 0;
        switch (type) {
            case "2":
                space = 60000 * 5;
                break;
            case "3":
                space = 60000 * 15;
                break;
            case "4":
                space = 60000 * 30;
                break;
            case "5":
                space = 60000 * 60;
                break;
            case "6":
                space = 60000 * 60 * 4;
                break;
        }

        Date current = new Date();
        Date quoteDate = new Date(current.getTime() - (current.getTime() % space));  //得到开始时间

        return sqlExecuteDao.findKlineNode(contract, quoteDate, new Date(quoteDate.getTime() + space));

    }

    /**
     * 处理跳点
     *
     * @param lines
     * @param openDate
     * @param contract
     * @return
     */
    private List<KLine> handleJume(List<KLine> lines, Date openDate, String contract) {
        List<KLine> openJumeList = new ArrayList<>();
        //开盘补全点数
        if (lines.size() > 0) {
            int openJume = (int) ((lines.get(0).getStartDate().getTime() - openDate.getTime()) / 60000);
            if (openJume > 0) {
                List<Date> openJumeDate = new ArrayList<>();
                //填时间
                for (int i = 0; i < openJume; i++) {
                    openJumeDate.add(new Date(openDate.getTime() + 60000 * i));
                }
                for (Date date : openJumeDate) {
                    openJumeList.add(emptyKLineNode(contract, date));
                }
            }
        }

        return openJumeList;
    }

    /**
     * 处理断点
     *
     * @param lines
     * @param contract
     * @return
     */
    private List<KLine> handleBreak(List<KLine> lines, String contract) {
        List<KLine> newLines = new ArrayList<>();
        Date previous = null; //上一个点的时间，用于处理跳点
        for (KLine quote : lines) {
            if (previous != null) {

                quote.getStartDate().setSeconds(0);

                //拿到跳点
                int jume = (int) ((quote.getStartDate().getTime() - previous.getTime()) / 60000);
                if (jume > 1) {
                    List<Date> jumeDate = new ArrayList<>();
                    //填时间
                    for (int i = 1; i < jume; i++) {
                        jumeDate.add(new Date(previous.getTime() + 60000 * i));
                    }

                    for (Date date : jumeDate) {
                        newLines.add(emptyKLineNode(contract, date));
                    }
                }
            }

            newLines.add(quote);
            previous = quote.getStartDate();
        }

        return newLines;
    }
}
